/******************************************************************************

    Copyright (C) 2018 ErisedMedia
    All Rights Reserved.

    This source code and any compilation or derivative thereof is the
    proprietary information of ErisedMedia and is
    confidential in nature.
    Under no circumstances is this software to be exposed to or placed under an
    Open Source License of any type without the expressed written permission of
    ErisedMedia.

******************************************************************************/

/******************************************************************************

    File:   Tools.cc
    Brief:  Tools实现，工具接口实现.

******************************************************************************/

/******************************************************************************

    Rev     Date        Author      Comments
--------------------------------------------------------------------------------
    001     20181120    RunyuanYe        Original

--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.

*******************************************************************************/


/******************************************************************************
*
*   INCLUDE FILES
*
******************************************************************************/

#include "Head.h"
#include "Tools.h"
#ifdef USE_UUID
#include "uuid/uuid.h"
#endif

/******************************************************************************
*
*    LOCAL MACROS
*
******************************************************************************/

#define USE_MS_PROFILE_TIME 0

/******************************************************************************
*
*    LOCAL TYPEDEFS
*
******************************************************************************/


/******************************************************************************
*
*    STATIC DATA
*
******************************************************************************/


/******************************************************************************
*
*    EXPORTED DATA
*
******************************************************************************/


/******************************************************************************
*
*    EXPORTED FUNCTION
*
******************************************************************************/


/******************************************************************************
*
*    STATIC FUNCTION PROTOTYPES
*
******************************************************************************/


/******************************************************************************
*
*    FUNCTION IMPLEMENTATION
*
******************************************************************************/

/*
    获取时钟Ticks，单位：毫秒

    返回时钟计数
*/
long long GetClockTicks()
{
    struct timespec tnow;
    long long curTime;

    if (!clock_gettime(CLOCK_MONOTONIC, &tnow))
    {
        curTime = (long long)(tnow.tv_sec)*1000;
        curTime += (tnow.tv_nsec/1000000);
    } else {
        curTime = 0ll;
    }

    return curTime;
}

/*
    获取时钟Ticks，单位：微秒

    返回时钟计数
*/
long long GetClockTicksEx()
{
    struct timespec tnow;
    long long curTime;

    if (!clock_gettime(CLOCK_MONOTONIC, &tnow))
    {
        curTime = (long long)(tnow.tv_sec)*1000000;
        curTime += (tnow.tv_nsec/1000);
    } else {
        curTime = 0ll;
    }

    return curTime;
}

/*
    获取文件大小

    file：为输入的文件

    成功返回文件大小，失败返回负值
*/
int GetFileSize(FILE* file)
{
    int fileSize;
    int curPos;

    curPos = ftell(file);
    fseek(file, 0, SEEK_END);
    fileSize = ftell(file);
    fseek(file, curPos, SEEK_SET);

    return fileSize;
}

/*
    获取文件大小

    fileName：为输入的文件名

    成功返回文件大小，失败返回负值
*/
int GetFileSizeEx(const char* fileName)
{
    int fileSize = -1;
    FILE* file;

    file = fopen(fileName, "rb");
    if(file)
    {
        fseek(file, 0, SEEK_END);
        fileSize = ftell(file);
        fclose(file);
    }

    return fileSize;
}


ProfileTime::ProfileTime(const char* infoString, int printFrameCount, bool needPrint) 
    : m_infoString(infoString), m_printFrameCount(printFrameCount), m_needPrint(needPrint)
{
    m_fps           = 0.0f;
    m_time          = 0.0f;
    m_totalTime     = 0;
    m_frameCount    = 0;
    m_intervalIsFirst = true;
    m_intervalFrameCount = 0;
    m_intervalStartTime =
    m_intervalEndTime =
    #if USE_MS_PROFILE_TIME
    m_startTime     = m_endTime = GetClockTicks();
    #else
    m_startTime     = m_endTime = GetClockTicksEx();
    #endif
}

ProfileTime::~ProfileTime()
{

}

void ProfileTime::StartTime()
{
    #if ENABLE_PROFILE_TIME_LOCK
    lock_guard<mutex> guard(m_lock);
    #endif
    #if USE_MS_PROFILE_TIME
    m_startTime = GetClockTicks();
    #else
    m_startTime = GetClockTicksEx();
    #endif
}

void ProfileTime::EndTime()
{
    #if ENABLE_PROFILE_TIME_LOCK
    lock_guard<mutex> guard(m_lock);
    #endif
    #if USE_MS_PROFILE_TIME
    m_endTime = GetClockTicks();
    #else
    m_endTime = GetClockTicksEx();
    #endif
    long long diffTime = m_endTime-m_startTime;
    m_totalTime += diffTime;
    m_frameCount++;
    if(m_frameCount==m_printFrameCount)
    {
        #if USE_MS_PROFILE_TIME
        m_fps = (float)(m_frameCount*1000)/(float)(m_totalTime);
        m_time= (float)(m_totalTime)/(float)(m_frameCount);
        if(m_needPrint)
        {
            cout << m_infoString << " - Fps: " << m_fps
                << ", Avg Frame Time:" << m_time << "ms, Cur Frame Time: "
                <<  (float)(diffTime) << "ms" << endl;
        }
        #else
        m_fps = (float)(m_frameCount*1000000)/(float)(m_totalTime);
        m_time= (float)(m_totalTime)/(float)(1000*m_frameCount);
        if(m_needPrint)
        {
            cout << m_infoString << " - Fps: " << m_fps
                << ", Avg Frame Time:" << m_time << "ms, Cur Frame Time: "
                <<  (float)(diffTime)/1000 << "ms" << endl;
        }
        #endif
        m_frameCount = 0;
        m_totalTime = 0;
    }
}

// time: ms
void ProfileTime::FpsIntervalTime(int time)
{
    #if ENABLE_PROFILE_TIME_LOCK
    lock_guard<mutex> guard(m_lock);
    #endif
    #if USE_MS_PROFILE_TIME
    m_intervalEndTime = GetClockTicks();
    #else
    m_intervalEndTime = GetClockTicksEx();
    #endif
    if(m_intervalIsFirst)
    {
        m_intervalIsFirst = false;
        return;
    }

    long long diffTime = m_intervalEndTime-m_intervalStartTime; 
    m_intervalFrameCount++;

    #if USE_MS_PROFILE_TIME
    if(diffTime > (long long)(time))
    #else
    if(diffTime > (long long)(time)*1000)
    #endif
    {
        #if USE_MS_PROFILE_TIME
        m_fps = (float)(1000*m_intervalFrameCount)/(float)(diffTime);
        m_time= (float)(diffTime)/(float)(m_intervalFrameCount);
        if(m_needPrint)
        {
            cout << m_infoString << " - Fps: " << m_fps
                << ", Frame Interval Time:" << m_time << "ms" << endl;
        }
        #else
        m_fps = (float)(1000000*m_intervalFrameCount)/(float)(diffTime);
        m_time= (float)(diffTime)/(float)(1000*m_intervalFrameCount);
        if(m_needPrint)
        {
            cout << m_infoString << " - Fps: " << m_fps
                << ", Frame Interval Time:" << m_time << "ms" << endl;
        }
        #endif
        m_intervalStartTime = m_intervalEndTime;
        m_intervalFrameCount = 0;
    }
}

float ProfileTime::GetFps() const
{
    return m_fps;
}
    
float ProfileTime::GetFrameTime() const
{
    return m_time;
}

#ifdef USE_UUID
/*
    生成UUID

    uuid：输出UUID字符串
*/
void GenerateUUID(char *uuid)
{
    uuid_t uu;
    uuid_generate(uu);
    uuid_unparse_lower(uu, uuid);
}

/*
    生成UUID

    uuid：输出UUID字符串
*/
void GenerateUUIDEx(std::string& uuid)
{
    uuid_t uu;
    char uuidTmp[40];
    uuid_generate(uu);
    uuid_unparse_lower(uu, uuidTmp);
    uuid.assign(uuidTmp);
}
#endif

/*
    获取文件名

    fullFileName： 带目录的完整路径名
    needExt： 是否需要返回文件名的扩展名

    返回获取的文件名
*/
std::string GetFileName(std::string fullFileName, bool needExt)
{
    if (fullFileName.empty())
    {
        return "";
    }

    auto iPos1 = fullFileName.find_last_of('/');
    auto iPos2 = fullFileName.find_last_of('\\');
    std::string::size_type iPos;
    if (iPos1 == std::string::npos)
    {
        if (iPos2 == std::string::npos)
        {
            iPos = 0;
        }
        else
        {
            iPos = iPos2 + 1;
        }
    }
    else
    {
        if (iPos2 == std::string::npos)
        {
            iPos = iPos1 + 1;
        }
        else
        {
            if(iPos1 < iPos2)
            {
                iPos = iPos2 + 1;
            }
            else
            {
                iPos = iPos1 + 1;
            }
        }
    }

    if(needExt)
    {
        return fullFileName.substr(iPos, fullFileName.length() - iPos);
    }
    else
    {
        auto endpos = fullFileName.find_last_of('.');
        if (endpos == std::string::npos)
        {
            return fullFileName.substr(iPos, fullFileName.length() - iPos);
        }

        return fullFileName.substr(iPos, endpos - iPos);
    }
}

/**
 * @brief Gets filename without extension
 * @param filepath - full file name
 * @return filename without extension
 */
std::string FileNameNoExt(const std::string &filepath) {
    auto pos = filepath.rfind('.');
    if (pos == std::string::npos) return filepath;
    return filepath.substr(0, pos);
}


void LongLongAssign(void* dst, const void* src)
{
    int* pdst = (int*)(dst);
    const int* psrc = (const int*)(src);
    pdst[0] = psrc[0];
    pdst[1] = psrc[1];
}

/*
    连接两个路径

    joinpath： 为输出的连接后的路径
    path1： 要连接的第1个路径
    path2： 要连接的第2个路径
*/
void JoinPath(std::string& joinpath, const std::string& path1, const std::string& path2)
{

    if(*path1.crbegin() == '/')
    {
        joinpath = path1 + path2;
    }
    else
    {
        joinpath = path1 + "/" + path2;
    }
}

/*
    连接两个路径

    path1： 要连接的第1个路径
    path2： 要连接的第2个路径

    返回连接后的路径
*/
std::string JoinPathEx(const std::string& path1, const std::string& path2)
{
    std::string joinpath;
    JoinPath(joinpath, path1, path2);
    return joinpath;
}